// uniCloud/cloudfunctions/RedPacketCloud/index.js
const db = uniCloud.database();
const dbCmd = db.command;
const redPacketsColl = db.collection('red_packet');
const locationConfigsColl = db.collection('red_packet_location');
const adConfigsColl = db.collection('red_packet_ad');
const usersColl = db.collection('linli-user');
const pageConfigsColl = db.collection('page_code_config');

/**
 * 红包系统核心云对象
 * 覆盖：创建/支付/领取/查询/过期处理全流程
 */
module.exports = {
  /**
   * 1. 创建红包（支持所有类型）
   * @param {Object} params - 参数
   * @param {String} params.packetType - 红包类型：personal/group/location/ad
   * @param {String} params.sendUserId - 发放者ID
   * @param {String} [params.targetId] - 目标ID（个人：接收者ID；群：群ID）
   * @param {Number} params.totalAmount - 总金额（≥20元）
   * @param {Number} params.totalCount - 红包个数
   * @param {String} params.singleType - 单个金额类型：fixed/random
   * @param {Number} [params.fixedSingleAmount] - 固定单个金额（singleType=fixed时必填）
   * @param {Object} [params.randomRange] - 随机金额范围（singleType=random时必填：{min, max}）
   * @param {String} [params.blessingText] - 祝福语（默认按类型填充）
   * @param {Object} [params.locationConfig] - 位置红包配置（packetType=location时必填）
   * @param {Object} [params.adConfig] - 广告红包配置（packetType=ad时必填）
   * @returns {Object} 红包主文档ID
   */
  async createRedPacket(params) {
    try {
      // 1. 基础参数校验
      const { packetType, sendUserId, totalAmount, totalCount, singleType } = params;
      if (!['personal', 'group', 'location', 'ad'].includes(packetType)) {
        return { success: false, msg: '红包类型无效' };
      }
      if (totalAmount < 20) {
        return { success: false, msg: '总金额不得小于20元' };
      }
      if (totalCount < 1) {
        return { success: false, msg: '红包个数不得小于1' };
      }

      // 2. 校验用户（是否存在+商户权限）
      const sendUser = await usersColl.doc(sendUserId).get();
      if (!sendUser.data) {
        return { success: false, msg: '发放者不存在' };
      }
      if (packetType === 'ad' && sendUser.data.userType !== 'merchant') {
        return { success: false, msg: '仅商户可发布广告红包' };
      }

      // 3. 处理不同类型红包的扩展配置
      let locationConfigId = null;
      let adConfigId = null;
      const defaultBlessing = {
        personal: '恭喜发财，大吉大利',
        group: '恭喜发财，大吉大利',
        location: '路过的小伙伴，快来领红包啦！',
        ad: '看广告领红包，福利多多～'
      };

      // 3.1 位置红包：先插入locationConfigs
      if (packetType === 'location') {
        const { locationConfig } = params;
        if (!locationConfig || !locationConfig.targetLocation) {
          return { success: false, msg: '位置红包需配置目标位置' };
        }
        const locationRes = await locationConfigsColl.add({
          targetLocation: locationConfig.targetLocation,
          visibleRange: locationConfig.visibleRange || 1000, // 默认1公里
          staySeconds: locationConfig.staySeconds || 10, // 默认停留10秒
          jumpPageCode: locationConfig.jumpPageCode,
          createTime: new Date()
        });
        locationConfigId = locationRes.id;
      }

      // 3.2 广告红包：先插入adConfigs
      if (packetType === 'ad') {
        const { adConfig } = params;
        if (!adConfig || !adConfig.adTitle || !adConfig.adMedia) {
          return { success: false, msg: '广告红包需配置标题和媒体' };
        }
        // 校验跳转页面是否存在
        const pageRes = await pageConfigsColl.where({
          pageCode: adConfig.jumpPageCode,
          status: true
        }).get();
        if (!pageRes.data.length) {
          return { success: false, msg: '广告跳转页面不存在或已禁用' };
        }
        const adRes = await adConfigsColl.add({
          adTitle: adConfig.adTitle,
          adMedia: adConfig.adMedia,
          jumpPageCode: adConfig.jumpPageCode,
          limitLocation: adConfig.limitLocation || false,
          locationConfig: adConfig.limitLocation ? adConfig.locationConfig : null,
          receiveCondition: adConfig.receiveCondition || { type: 'staySeconds', staySeconds: 10 },
          createTime: new Date()
        });
        adConfigId = adRes.id;
      }

      // 4. 插入红包主文档（初始状态：待支付）
      const redPacketRes = await redPacketsColl.add({
        packetType,
        sendUserId,
        targetId: params.targetId || null,
        totalAmount,
        totalCount,
        singleType,
        fixedSingleAmount: singleType === 'fixed' ? params.fixedSingleAmount : null,
        randomRange: singleType === 'random' ? params.randomRange : null,
        blessingText: params.blessingText || defaultBlessing[packetType],
        status: 'pendingPay', // 待支付
        expireTime: new Date(Date.now() + 24 * 60 * 60 * 1000), // 默认24小时过期
        locationConfigId,
        adConfigId,
        paymentRecord: {
          payMethod: null,
          payStatus: 'pending',
          transactionId: null,
          payTime: null,
          failReason: null
        },
        receiveRecords: [],
        createTime: new Date(),
        updateTime: new Date()
      });

      // 5. 更新用户发送红包统计（初始+1）
      await usersColl.doc(sendUserId).update({
        'redPacketStats.sentCount': dbCmd.inc(1),
        updateTime: new Date()
      });

      return {
        success: true,
        data: {
          redPacketId: redPacketRes.id,
          status: 'pendingPay'
        }
      };
    } catch (err) {
      console.error('createRedPacket error:', err);
      return { success: false, msg: '创建红包失败', err: err.message };
    }
  },

  /**
   * 2. 支付红包（扣减余额/更新状态）
   * @param {Object} params - 参数
   * @param {String} params.redPacketId - 红包ID
   * @param {String} params.sendUserId - 发放者ID
   * @param {String} params.payMethod - 支付方式：balance/wechat/alipay
   * @returns {Object} 支付结果
   */
  async payRedPacket(params) {
    const { redPacketId, sendUserId, payMethod } = params;
    const transaction = await db.startTransaction(); // 开启事务

    try {
      // 1. 校验红包状态（必须是待支付）
      const redPacket = await transaction.collection('redPackets').doc(redPacketId).get();
      if (!redPacket.data) {
        await transaction.rollback();
        return { success: false, msg: '红包不存在' };
      }
      if (redPacket.data.status !== 'pendingPay') {
        await transaction.rollback();
        return { success: false, msg: '红包状态异常，无法支付' };
      }
      if (redPacket.data.sendUserId !== sendUserId) {
        await transaction.rollback();
        return { success: false, msg: '无支付权限' };
      }

      // 2. 扣减用户余额（仅余额支付需处理，微信/支付宝走第三方）
      const { totalAmount } = redPacket.data;
      if (payMethod === 'balance') {
        const userRes = await transaction.collection('users').doc(sendUserId).update({
          balance: dbCmd.inc(-totalAmount),
          updateTime: new Date()
        });
        if (userRes.modifiedCount === 0) {
          await transaction.rollback();
          return { success: false, msg: '余额不足或用户不存在' };
        }
        // 更新用户发送金额统计
        await transaction.collection('users').doc(sendUserId).update({
          'redPacketStats.sentAmount': dbCmd.inc(totalAmount)
        });
      }

      // 3. 更新红包支付记录和状态
      const payTime = new Date();
      await transaction.collection('redPackets').doc(redPacketId).update({
        status: 'active', // 支付成功，变为可领取
        'paymentRecord': {
          payMethod,
          payStatus: 'success',
          transactionId: `TRX${Date.now()}${Math.floor(Math.random() * 1000)}`, // 模拟流水号
          payTime,
          failReason: null
        },
        updateTime: payTime
      });

      await transaction.commit(); // 提交事务
      return { success: true, data: { payTime, transactionId: 'TRX...' } };
    } catch (err) {
      await transaction.rollback(); // 回滚事务
      console.error('payRedPacket error:', err);
      return { success: false, msg: '支付失败', err: err.message };
    }
  },

  /**
   * 3. 领取红包（核心方法，含多类型校验）
   * @param {Object} params - 参数
   * @param {String} params.redPacketId - 红包ID
   * @param {String} params.receiveUserId - 领取者ID
   * @param {Object} [params.receiveLocation] - 领取位置（位置/广告红包必填）
   * @param {Number} [params.adStayTime] - 广告停留秒数（广告红包必填）
   * @returns {Object} 领取结果（金额/跳转页面）
   */
  async receiveRedPacket(params) {
    const { redPacketId, receiveUserId, receiveLocation, adStayTime } = params;
    const transaction = await db.startTransaction();

    try {
      // 1. 校验红包基本状态
      const redPacket = await transaction.collection('redPackets').doc(redPacketId).get();
      if (!redPacket.data) {
        await transaction.rollback();
        return { success: false, msg: '红包不存在' };
      }
      const {
        status, expireTime, totalAmount, totalCount, singleType,
        fixedSingleAmount, randomRange, packetType, locationConfigId, adConfigId,
        receiveRecords, sendUserId
      } = redPacket.data;

      // 状态校验：必须可领取、未过期、未领完
      if (status !== 'active') {
        await transaction.rollback();
        return { success: false, msg: '红包已失效' };
      }
      if (new Date() > new Date(expireTime)) {
        await transaction.rollback();
        return { success: false, msg: '红包已过期' };
      }
      if (receiveRecords.length >= totalCount) {
        await transaction.rollback();
        return { success: false, msg: '红包已领完' };
      }
      // 自己不能领自己的红包
      if (receiveUserId === sendUserId) {
        await transaction.rollback();
        return { success: false, msg: '不能领取自己的红包' };
      }
      // 已领取过不能再领
      const hasReceived = receiveRecords.some(item => item.receiveUserId === receiveUserId);
      if (hasReceived) {
        await transaction.rollback();
        return { success: false, msg: '已领取过该红包' };
      }

      // 2. 按红包类型校验领取条件
      let jumpPageCode = '';
      if (packetType === 'location') {
        // 位置红包：校验距离
        const locationConfig = await transaction.collection('locationConfigs').doc(locationConfigId).get();
        const { targetLocation, visibleRange, staySeconds } = locationConfig.data;
        // 计算用户与目标位置的直线距离（米）
        const distance = this.calculateDistance(
          receiveLocation.latitude, receiveLocation.longitude,
          targetLocation.latitude, targetLocation.longitude
        );
        if (distance > visibleRange) {
          await transaction.rollback();
          return { success: false, msg: '超出红包领取范围' };
        }
        jumpPageCode = locationConfig.data.jumpPageCode;
      } else if (packetType === 'ad') {
        // 广告红包：校验停留时间
        const adConfig = await transaction.collection('adConfigs').doc(adConfigId).get();
        const { receiveCondition, jumpPageCode: adJumpCode } = adConfig.data;
        if (receiveCondition.type === 'staySeconds' && adStayTime < receiveCondition.staySeconds) {
          await transaction.rollback();
          return { success: false, msg: '广告停留时间不足' };
        }
        // 校验位置（若限定）
        if (adConfig.data.limitLocation) {
          const { targetLatitude, targetLongitude, visibleRange } = adConfig.data.locationConfig;
          const distance = this.calculateDistance(
            receiveLocation.latitude, receiveLocation.longitude,
            targetLatitude, targetLongitude
          );
          if (distance > visibleRange) {
            await transaction.rollback();
            return { success: false, msg: '超出广告红包领取范围' };
          }
        }
        jumpPageCode = adJumpCode;
      }

      // 3. 计算领取金额
      let receiveAmount = 0;
      if (singleType === 'fixed') {
        receiveAmount = fixedSingleAmount;
      } else {
        // 随机金额：确保剩余金额足够（最后一个红包领完剩余）
        const receivedTotal = receiveRecords.reduce((sum, item) => sum + item.receiveAmount, 0);
        const remainingAmount = totalAmount - receivedTotal;
        const remainingCount = totalCount - receiveRecords.length;

        if (remainingCount === 1) {
          receiveAmount = remainingAmount; // 最后一个红包领完剩余
        } else {
          // 随机范围：min ≤ 金额 ≤ max，且剩余金额 ≥ (remainingCount-1)*min
          const min = randomRange.min;
          const max = Math.min(randomRange.max, remainingAmount - (remainingCount - 1) * min);
          receiveAmount = Math.round((Math.random() * (max - min) + min) * 100) / 100; // 保留2位小数
        }
      }

      // 4. 生成领取记录
      const receiveRecord = {
        receiveUserId,
        receiveAmount,
        receiveTime: new Date(),
        receiveLocation: receiveLocation || null,
        adStayTime: adStayTime || null,
        jumpPageCode,
        status: 'success',
        failReason: null
      };

      // 5. 更新红包（添加领取记录+判断是否领完）
      const newStatus = receiveRecords.length + 1 === totalCount ? 'closed' : 'active';
      await transaction.collection('redPackets').doc(redPacketId).update({
        receiveRecords: dbCmd.push(receiveRecord),
        status: newStatus,
        updateTime: new Date()
      });

      // 6. 增加用户余额和领取统计
      await transaction.collection('users').doc(receiveUserId).update({
        balance: dbCmd.inc(receiveAmount),
        'redPacketStats.receivedCount': dbCmd.inc(1),
        'redPacketStats.receivedAmount': dbCmd.inc(receiveAmount),
        updateTime: new Date()
      });

      await transaction.commit();
      return {
        success: true,
        data: {
          receiveAmount,
          jumpPageCode,
          receiveTime: receiveRecord.receiveTime
        }
      };
    } catch (err) {
      await transaction.rollback();
      console.error('receiveRedPacket error:', err);
      return { success: false, msg: '领取红包失败', err: err.message };
    }
  },

  /**
   * 4. 查询用户附近的位置红包
   * @param {Object} params - 参数
   * @param {Number} params.latitude - 用户纬度
   * @param {Number} params.longitude - 用户经度
   * @param {Number} [params.maxDistance=5000] - 最大查询距离（米）
   * @returns {Array} 可领取的位置红包列表
   */
  async getNearbyLocationRedPackets(params) {
    try {
      const { latitude, longitude, maxDistance = 5000 } = params;

      // 1. 查用户附近的位置配置（地理空间查询）
      const locationConfigs = await locationConfigsColl.find({
        targetLocation: dbCmd.geoNear({
          geometry: { type: 'Point', coordinates: [longitude, latitude] }, // MongoDB：经度在前
          maxDistance: maxDistance,
          spherical: true
        })
      }).toArray();

      if (!locationConfigs.length) {
        return { success: true, data: [] };
      }

      // 2. 查对应的可领取红包
      const locationConfigIds = locationConfigs.map(config => config._id);
      const redPackets = await redPacketsColl.find({
        packetType: 'location',
        locationConfigId: dbCmd.in(locationConfigIds),
        status: 'active',
        expireTime: dbCmd.gt(new Date())
      }).field({
        _id: 1,
        blessingText: 1,
        totalAmount: 1,
        totalCount: 1,
        receiveRecords: { $slice: 1 }, // 只返回1条领取记录（减少数据量）
        locationConfigId: 1,
        createTime: 1
      }).toArray();

      // 3. 关联位置距离信息
      const result = redPackets.map(packet => {
        const config = locationConfigs.find(c => c._id === packet.locationConfigId);
        const distance = this.calculateDistance(
          latitude, longitude,
          config.targetLocation.latitude, config.targetLocation.longitude
        );
        return {
          ...packet,
          distance: Math.round(distance), // 距离（米，四舍五入）
          visibleRange: config.visibleRange
        };
      });

      return { success: true, data: result };
    } catch (err) {
      console.error('getNearbyLocationRedPackets error:', err);
      return { success: false, msg: '查询附近红包失败', err: err.message };
    }
  },

  /**
   * 5. 处理过期红包（定时任务调用）
   * @returns {Object} 处理结果（过期个数、退回金额）
   */
  async handleExpiredRedPackets() {
    try {
      // 1. 查所有过期未处理的红包
      const expiredRedPackets = await redPacketsColl.find({
        status: dbCmd.in(['active', 'pendingPay']),
        expireTime: dbCmd.lt(new Date())
      }).toArray();

      if (!expiredRedPackets.length) {
        return { success: true, data: { expiredCount: 0, refundAmount: 0 } };
      }

      let refundTotal = 0;
      const transaction = await db.startTransaction();

      // 2. 批量处理过期红包
      for (const packet of expiredRedPackets) {
        const { _id, sendUserId, totalAmount, receiveRecords, status } = packet;

        // 2.1 更新红包状态为过期
        await transaction.collection('redPackets').doc(_id).update({
          status: 'expired',
          updateTime: new Date()
        });

        // 2.2 计算未领取金额并退回（仅支付过的红包需退回）
        if (status === 'active') {
          const receivedTotal = receiveRecords.reduce((sum, item) => sum + item.receiveAmount, 0);
          const refundAmount = totalAmount - receivedTotal;
          if (refundAmount > 0) {
            // 退回发放者余额
            await transaction.collection('users').doc(sendUserId).update({
              balance: dbCmd.inc(refundAmount),
              updateTime: new Date()
            });
            // 修正用户发送金额统计
            await transaction.collection('users').doc(sendUserId).update({
              'redPacketStats.sentAmount': dbCmd.inc(-refundAmount)
            });
            refundTotal += refundAmount;
          }
        }
      }

      await transaction.commit();
      return {
        success: true,
        data: {
          expiredCount: expiredRedPackets.length,
          refundAmount: refundTotal
        }
      };
    } catch (err) {
      console.error('handleExpiredRedPackets error:', err);
      return { success: false, msg: '处理过期红包失败', err: err.message };
    }
  },

  /**
   * 辅助方法：计算两点间直线距离（米）
   * @param {Number} lat1 - 点1纬度
   * @param {Number} lng1 - 点1经度
   * @param {Number} lat2 - 点2纬度
   * @param {Number} lng2 - 点2经度
   * @returns {Number} 距离（米）
   */
  calculateDistance(lat1, lng1, lat2, lng2) {
    const R = 6371000; // 地球半径（米）
    const radLat1 = lat1 * Math.PI / 180;
    const radLat2 = lat2 * Math.PI / 180;
    const deltaLat = radLat2 - radLat1;
    const deltaLng = (lng2 - lng1) * Math.PI / 180;
    const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
      Math.cos(radLat1) * Math.cos(radLat2) *
      Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
  }
};